import { Service } from 'egg';
import { promisify } from 'util';

interface TOption {
  name: string;
  key: string;
  state: string;
  options: TOption[];
}

/**
 * 复制账户下的角色，部门及座席
 */
export default class AccountDataCopyService extends Service {
  public async accountDataCopy({ originalAccount, originalPBX, account, pbx, sessionId }: {
    originalAccount: string;
    account: string;
    pbx: string;
    sessionId: string;
    originalPBX?: string;
  }) {
    const { app, ctx } = this;

    if (!(originalAccount && account && pbx && sessionId) || originalAccount === account) {
      throw new Error('Parameter validation error');
    }
    const agentFilter: any = { account: originalAccount, status: { $ne: 'disable' } };
    if (originalPBX) {
      agentFilter.pbx = originalPBX;
    }
    // 查询原账户数据
    const [roles, departments, agents] = await Promise.all([
      app.commonDB.db().collection(app.constants.COLL_APP_ROLE).find({ accountId: originalAccount }).toArray(),
      app.commonDB.db(app.constants.BILL).collection(app.constants.COLL_PLATFORM_DEPARTMENT)
        .find({ account: originalAccount }).toArray(),
      app.commonDB.db(app.constants.BILL).collection(app.constants.COLL_PLATFORM_USER)
        .find(agentFilter).toArray(),
    ]);

    const rolemap = this.roleCopy(roles, account);
    const deptmap = this.departmentCopy(departments, account);

    // 删除默认的角色
    await app.commonDB.db().collection(app.constants.COLL_APP_ROLE).updateMany({ accountId: account }, {
      $set: {
        accountId: account + '_bak',
      },
    });
    await app.commonDB.db().collection(app.constants.COLL_APP_ROLE).insertMany(roles);
    ctx.logger.info('>>>>>>>>>>>>>>>>>>>> Roles inserted');

    const { usermap, gatewayExtenAgents } = await this.batchAddAgents({ agents, sessionId, account, pbx, rolemap, deptmap });
    ctx.logger.info('Agents inserted');
    await this.batchBindGatewayExten({ account, pbx, gatewayExtenAgents }, sessionId);

    ctx.logger.info('>>>>>>>>>>>>>>>>>>>> Bind gateway sip completed');

    this.updateDepartmentObserver(departments, usermap);
    // 删除默认的部门
    await app.commonDB.db(app.constants.BILL).collection(app.constants.COLL_PLATFORM_DEPARTMENT).updateMany({ account }, {
      $set: {
        account: account + '_bak',
      },
    });
    await app.commonDB.db(app.constants.BILL).collection(app.constants.COLL_PLATFORM_DEPARTMENT).insertMany(departments);
    ctx.logger.info('>>>>>>>>>>>>>>>>>>>> Departments inserted');

    // await this.businessCopy({ originalAccount, account, usermap, rolemap })
  }

  public roleCopy(roles, account) {
    const { app } = this;
    const map = new Map();
    for (const role of roles) {
      const id = app.m7Utils.UUID.generateUuid();
      map.set(role._id, id);
      role._id = id;
      role.accountId = account;
    }
    return map;
  }

  public departmentCopy(departments, account) {
    const { app }  = this;
    const map = new Map();
    for (const dept of departments)  {
      const id = app.m7Utils.UUID.generateUuid();
      map.set(dept._id, id);
      dept._id = id;
      dept.account = account;
    }
    for (const dept of departments) {
      if (dept.pId) {
        dept.pId = map.get(dept.pId);
      }
      dept.treePath = dept.treePath.split('/').map((deptId) => map.get(deptId)).filter((deptId) => !!deptId).join('/') + '/';
    }
    return map;
  }

  public async batchAddAgents({ agents, sessionId, account, pbx, rolemap, deptmap }) {
    const { app, ctx } = this;
    // 新旧座席ID的映射
    const usermap = new Map();
    // 需要绑定网关sip号的座席ID
    let gatewayExtenAgents: string[] = [];
    const primaries: any[] = [];
    const secondaries: any[] = [];
    const result: any = { addedExtens: [], failExtens: [] };
    for (const agent of agents) {
      // 座席的原始ID
      const agentId = agent._id;
      agent.action = 'add';
      agent.Account = account;
      agent.PBX = pbx;
      if (agent.role && agent.role.length > 0) {
        agent.role = agent.role.map((r) => rolemap.get(r));
      }
      if (agent.deptId) {
        agent.deptId = deptmap.get(agent.deptId);
      }
      if (agent.gatewayExten) {
        gatewayExtenAgents.push(agentId);
      }

      Object.assign(agent, {
        sipExten: '',
        mobileExten: '',
        gatewayExten: '',
      });
      if (agent.inheritedFrom) {
        secondaries.push(agent);
      } else {
        primaries.push(agent);
      }
    }
    for (const agent of primaries)  {
      const res1 = await this.requestYunkefu({
        action: 'config.ivr.agent.updateAgent',
        data: JSON.stringify(agent),
        sessionId,
      });
      ctx.logger.info('Add agent response', res1);
      usermap.set(agent._id, res1?.data?._id);
      if (res1.success) {
        result.addedExtens.push(agent.exten);
      } else {
        result.failExtens.push(agent.exten);
      }
    }

    for (const agent of secondaries)  {
      agent.inheritedFrom = usermap.get(agent.inheritedFrom);
      const res2 = await this.requestYunkefu({
        action: 'config.ivr.agent.updateAgent',
        data: JSON.stringify(agent),
        sessionId,
      });
      ctx.logger.info('Add agent response', res2);
      usermap.set(agent._id, res2?.data?._id);
      if (res2.success) {
        result.addedExtens.push(agent.exten);
      } else {
        result.failExtens.push(agent.exten);
      }
    }
    ctx.logger.info('user map', usermap);
    ctx.logger.info('>>>>>>>>>>>>>>>> Batch add agents result', result);

    const ops: any[] = [];
    // 更新上级
    for (const agent of agents) {
      if (agent.parentId) {
        ops.push({ updateOne: {
          filter: { _id: usermap.get(agent._id) },
          update: { $set: { parentId: usermap.get(agent.parentId)} },
        }});
      }
    }
    const res = await app.commonDB.db(app.constants.BILL).collection(app.constants.COLL_PLATFORM_USER).bulkWrite(ops);
    ctx.logger.info('>>>>>>>>>>>>>>>> Update parentId response', res);
    gatewayExtenAgents = gatewayExtenAgents.map((agentId) => usermap.get(agentId));

    return { usermap, gatewayExtenAgents };
  }

  public async batchBindGatewayExten({ account, pbx, gatewayExtenAgents }, sessionId) {
    const { app } = this;
    let gatewaySips: Array<{ _id: string; Exten: string }> = [];
      // 获取可用的网关Sip号
    gatewaySips = await app.commonDB.db().collection(app.constants.COLL_APP_EXTENSION_CONFIG)
      .find({
        Account: account,
        PBX: pbx,
        ExtenType: 'gateway',
        UserID: { $in: [null, ''] },
      }).sort({ UpdateTime: -1 })
      .project({ _id: 1, Exten: 1 })
      .toArray();
    if (gatewaySips.length < gatewayExtenAgents.length) {
      const amount = gatewayExtenAgents.length - gatewaySips.length;
      const res = await this.requestYunkefu({
        action: 'config.ivr.sip.batchAddGatewaySip',
        data: JSON.stringify({
          Account: account,
          PBX: pbx,
          Amount: amount,
          IsSeries: false,
        }),
        sessionId,
      });
      if (res.success) {
        this.ctx.logger.info('Batch add gateway sip success', amount);
      } else {
        this.ctx.logger.info('Batch add gateway response', res);
        throw new Error('Batch add gateway sip fail');
      }
    }

    let waitCount = 0;
    do {
      this.ctx.logger.info('Waiting 5 seconds for gateway sips to be available');
      await (promisify(setTimeout))(5000);
      if (waitCount > 12) {
        this.ctx.logger.warn('Timeout 60s, gateway sips');
      }
      if (waitCount > 18) {
        this.ctx.logger.warn('Timeout 90s, gateway sips');
        throw new Error('Timeout');
      }
      // 获取可用的网关Sip号
      gatewaySips = await app.commonDB.db().collection(app.constants.COLL_APP_EXTENSION_CONFIG)
        .find({
          Account: account,
          PBX: pbx,
          ExtenType: 'gateway',
          UserID: { $in: [null, ''] },
        }).sort({ UpdateTime: -1 })
        .project({ _id: 1, Exten: 1 })
        .toArray();
      waitCount++;
    } while (gatewaySips.length < gatewayExtenAgents.length);

    this.ctx.logger.info('Gateway sips are available');

    for (const uid of gatewayExtenAgents) {
      const gatewayExten = (gatewaySips.shift())?.Exten;
      const { success } = await this.requestYunkefu({
        action: 'config.ivr.sip.bindGatewayUserExten',
        data: JSON.stringify({
          _id: gatewayExten,
          UserID: uid,
          PBX: pbx,
        }),
        sessionId,
      });
      if (success) {
        this.ctx.logger.info(`Bind gateway success ${gatewayExten} -> ${uid}`);
      } else {
        this.ctx.logger.warn(`Bind gateway fail ${gatewayExten} -> ${uid}`);
      }
    }
  }

  public async requestYunkefu(data, options = {}) {
    const { ctx } = this;
    // const ccServerInUrl = 'https://kf.7moor.com/action';
    const ccServerInUrl = 'https://test.7moor.com:9100/action';
    const opt: any = {
      method: 'POST',
      contentType: 'json',
      dataType: 'json',
      data,
      timeout: this.app.constants.HTTP_REQUEST_DEFAULT_TIMEOUT,
      ...options,
    };
    const resp = await ctx.curl(ccServerInUrl, opt);
    return resp.data;
  }

  public updateDepartmentObserver(departments, usermap) {
    for (const dept of departments) {
      if (dept.observer && dept.observer.length > 0) {
        dept.observer = dept.observer.map((uid) => usermap.get(uid)).filter((uid) => !!uid);
      }
    }
  }

  // 复制字典
  public optiondicCopy(optiondics, account) {
    const map = new Map();
    for (const doc of optiondics) {
      const id = this.app.m7Utils.UUID.generateRandomId();
      map.set(doc._id, id);
      Object.assign(doc, {
        _id: id,
        accountId: account,
      });
      this.updateKeyOfOptions(doc.options, map);
    }
    return map;
  }

  // 复制工单字段
  public businessFlowFieldsCopy({ flowFields, flowmap, dictmap }) {
    const { app } = this;
    const map = new Map();
    for (const field of flowFields) {
      const id = app.m7Utils.UUID.generateUuid();
      map.set(field._id, id);
      field._id = id;
      field.updateTimeArr = [];
      field.flow_id = flowmap.get(field.flow_id) || '';
      // 使用字典
      if (field.dic && dictmap.get(field.dic)) {
        field.dic = dictmap.get(field.dic);
      }
      // 默认值
      if (field.default && dictmap.get(field.default)) {
        field.default = dictmap.get(field.default);
      }
    }
    return map;
  }

  // 复制工单步骤
  public businessFlowStepCopy({ flowSteps, flowmap, fieldmap, rolemap, usermap }) {
    const { app } = this;
    const map = new Map();
    for (const step of flowSteps) {
      const id = app.m7Utils.UUID.generateUuid();
      map.set(step._id, id);
      step._id = id;
      step.createUser = '';
      step.flow_id = flowmap.get(step.flow_id) || '';

      // 提醒成员
      if (step.notifyUser && step.notifyUser.length > 0) {
        step.notifyUser = step.notifyUser.map((uid) => usermap.get(uid)).filter((uid) => !!uid);
      }

      // 步骤字段
      if (step.stepFields && step.stepFields.length > 0) {
        step.stepFields.forEach((stepField) => {
          stepField.cols.forEach((item) => {
            item.fields.forEach((field) => {
              field._id = fieldmap.get(field._id);
            });
          });
        });
      }
      // 步骤动作
      if (step.actions && step.actions.length > 0) {
        step.actions.forEach((action) => {
          action._id = app.m7Utils.UUID.generateUuid();
          // 动作角色
          if (action.actionRole) {
            action.actionRole = rolemap.get(action.actionRole);
          }
          if (action.actionFields) {
            action.actionFields.forEach((actionField) => {
              actionField.cols.forEach((item) => {
                item.fields.forEach((field) => {
                  field._id = fieldmap.get(field._id);
                });
              });
            });
          }
        });
      }
    }
    // 更新转入步骤
    for (const step of flowSteps) {
      if (step.actions && step.actions.length > 0) {
        step.actions.forEach((action) => {
          if (action.jumpTo) {
            action.jumpTo = map.get(action.jumpTo);
          }
        });
      }
    }
    return map;
  }

  // 复制工单流程
  public businessFlowCopy({ flows, account }) {
    const { app } = this;
    const map = new Map();
    for (const flow of flows) {
      const id = app.m7Utils.UUID.generateUuid();
      map.set(flow._id, id);
      flow._id = id;
      flow.account = account;
      flow.updateTimeArr = [];
      flow.createUser = '';
    }
    return map;
  }

  public businessFlowCopy2({ flows, usermap, fieldmap }) {
    const { app } = this;
    for (const flow of flows) {
      // 发送失败后的提醒人
      if (flow.notify && flow.notify.failureNotify && flow.notify.failureNotify.length > 0) {
        flow.notify.failureNotify = flow.notify.failureNotify.map((uid) => usermap.get(uid)).filter((uid) => !!uid);
      }

      // 列表卡片展示字段
      if (flow.showFields && flow.showFields.length > 0) {
        flow.showFields = flow.showFields.map((field) => fieldmap.get(field)).filter((field) => !!field);
      }

      // 触发器
      if (flow.triggers && flow.triggers.length > 0) {
        flow.triggers.forEach((trigger) => {
          trigger.triggerId = app.m7Utils.UUID.generateUuid();
          trigger.createUser = '';
          trigger.updateUser = '';
          if (trigger.actions && trigger.actions.length > 0) {
            trigger.actions.forEach((action) => {
              if (action.notify && action.notify.length > 0) {
                action.notify = action.notify.map((agentId) => usermap.get(agentId)).filter((agentId) => !!agentId);
              }
              if (action.agent && action.agent.length > 0) {
                action.agent = action.agent.map((agentId) => usermap.get(agentId)).filter((agentId) => !!agentId);
              }
            });
          }
          if (trigger.all && trigger.all.length > 0) {
            copyDataAndFiledName(trigger.all);
          }
          if (trigger.anyOne && trigger.anyOne.length > 0) {
            copyDataAndFiledName(trigger.anyOne);
          }
        });
      }
    }

    function copyDataAndFiledName(items) {
      items.forEach((item) => {
        if (item.type === 'customerFields') {
          const oldId: string = item.fieldId;
          const newId: string = fieldmap[oldId];
          item.fieldId = newId;
          // 处理fieldName
          // 先将其转为字符串， 使用replace全局替换 ，然后在切割成数组
          let fieldName: any = item.fieldName || [];
          fieldName = fieldName.toString();
          fieldName = fieldName.replace(new RegExp(oldId, 'g'), newId);
          fieldName = fieldName.split(',');
          // 处理data
          const data: any = {};
          for (const i in item.data) {
            if (i.indexOf(oldId) > -1) {
              const key: string = i.replace(oldId, newId);
              data[key] = item.data[i];
            }
          }
          // 将data和fieldName赋值给item
          item.data = data;
          item.fieldName = fieldName;
        }
      });
    }
  }

  public async businessCopy({ rolemap, usermap, account, originalAccount, }) {
    const { app, ctx } = this;
    const [optiondics, flows] = await Promise.all([
      app.commonDB.db().collection(app.constants.COLL_OPTIONDIC).find({accountId: originalAccount }).toArray(),
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW).find({ account: originalAccount }).toArray(),
    ]);
    const flowIdList = flows.map((flow) => flow._id);
    const [steps, fields] = await Promise.all([
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW_STEP).find({ flow_id: { $in: flowIdList } }).toArray(),
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW_FIELD).find({ flow_id: { $in: flowIdList } }).toArray(),
    ]);

    let dictmap = new Map();
    if (optiondics.length > 0) {
      dictmap = this.optiondicCopy(optiondics, account);
      // 删除默认的字典
      await app.commonDB.db().collection(app.constants.COLL_OPTIONDIC).updateMany({ accountId: account }, {
        $set: {
          accountId: account + '_bak',
        },
      });
      await app.commonDB.db().collection(app.constants.COLL_OPTIONDIC).insertMany(optiondics);
      ctx.logger.info('>>>>>>>>>>>>>>>>>> Optiondics inserted');
    }

    const flowmap = this.businessFlowCopy({ flows, account });
    const fieldmap = this.businessFlowFieldsCopy({ flowFields: fields, flowmap, dictmap });
    this.businessFlowCopy2({ flows, usermap, fieldmap });
    this.businessFlowStepCopy({ flowSteps: steps, flowmap, fieldmap, rolemap, usermap });
    await Promise.all([
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW_STEP).insertMany(steps),
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW_FIELD).insertMany(fields),
      app.commonDB.db().collection(app.constants.COLL_BUSINESS_FLOW).insertMany(flows),
    ]);
    ctx.logger.info('>>>>>>>>>>>>>>>>>>> Business configurations inserted');
  }

  private updateKeyOfOptions(options: TOption[], map) {
    if (!options || options.length === 0) {
      return;
    }
    options.forEach((opt) => {
      const key = this.app.m7Utils.UUID.generateUuid();
      map.set(opt.key, key);
      opt.key = key;
      this.updateKeyOfOptions(opt.options, map);
    });
  }
}
